package com.ryan.ohos.extension;

import ohos.agp.components.Component;
import ohos.agp.components.Component.EstimateSpec;
import ohos.agp.components.ComponentContainer;

/**
 * Estimate spec帮助类
 */
public class EstimateHelper {

    /**
     * 根据size与mode生成Estimate Spec
     * @param size component期望的大小
     * @param mode component Estimate的模式
     * @return Estimate Spec
     */
    public static int makeEstimateSpec(int size, int mode) {
        return (size & ~EstimateSpec.ESTIMATED_STATE_BIT_MASK) | (mode & EstimateSpec.ESTIMATED_STATE_BIT_MASK);
    }

    /**
     * 根据父component的spec、padding以及子component的期望大小，生成子component的spec
     * @param spec 父component的spec
     * @param padding 父component的padding
     * @param childDimension 子component的期望大小
     * @return 子component的spec
     */
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = EstimateSpec.getMode(spec);
        int specSize = EstimateSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
            // Parent has imposed an exact size on us
            case EstimateSpec.PRECISE:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = EstimateSpec.PRECISE;
                } else if (childDimension == ComponentContainer.LayoutConfig.MATCH_PARENT) {
                    // Child wants to be our size. So be it.
                    resultSize = size;
                    resultMode = EstimateSpec.PRECISE;
                } else if (childDimension == ComponentContainer.LayoutConfig.MATCH_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = EstimateSpec.NOT_EXCEED;
                    if (size == 0) {
                        // size will not be 0 when resultMode is NOT_EXCEED, don't know why
                        resultMode = EstimateSpec.PRECISE;
                    }
                }
                break;

            // Parent has imposed a maximum size on us
            case EstimateSpec.NOT_EXCEED:
                if (childDimension >= 0) {
                    // Child wants a specific size... so be it
                    resultSize = childDimension;
                    resultMode = EstimateSpec.PRECISE;
                } else if (childDimension == ComponentContainer.LayoutConfig.MATCH_PARENT) {
                    // Child wants to be our size, but our size is not fixed.
                    // Constrain child to not be bigger than us.
                    resultSize = size;
                    resultMode = EstimateSpec.NOT_EXCEED;
                } else if (childDimension == ComponentContainer.LayoutConfig.MATCH_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = EstimateSpec.NOT_EXCEED;
                    if (size == 0) {
                        // size will not be 0 when resultMode is NOT_EXCEED, don't know why
                        resultMode = EstimateSpec.PRECISE;
                    }
                }
                break;

            // Parent asked to see how big we want to be
            case EstimateSpec.UNCONSTRAINT:
                if (childDimension >= 0) {
                    // Child wants a specific size... let him have it
                    resultSize = childDimension;
                    resultMode = EstimateSpec.PRECISE;
                } else if (childDimension == ComponentContainer.LayoutConfig.MATCH_PARENT) {
                    // Child wants to be our size... find out how big it should
                    // be
                    resultSize = size;
                    resultMode = EstimateSpec.UNCONSTRAINT;
                } else if (childDimension == ComponentContainer.LayoutConfig.MATCH_CONTENT) {
                    // Child wants to determine its own size.... find out how
                    // big it should be
                    resultSize = size;
                    resultMode = EstimateSpec.UNCONSTRAINT;
                }
                break;
        }

        //noinspection ResourceType
        return makeEstimateSpec(resultSize, resultMode);
    }

    public static int resolveSizeAndState(int size, int measureSpec) {
        final int specMode = EstimateSpec.getMode(measureSpec);
        final int specSize = EstimateSpec.getSize(measureSpec);
        final int result;
        switch (specMode) {
            case EstimateSpec.NOT_EXCEED:
                if (specSize < size) {
                    result = specSize;
                } else {
                    result = size;
                }
                break;
            case EstimateSpec.PRECISE:
                result = specSize;
                break;
            case EstimateSpec.UNCONSTRAINT:
            default:
                result = size;
        }
        return result;
    }

    /**
     * Utility to return a default size. Uses the supplied size if the
     * MeasureSpec imposed no constraints. Will get larger if allowed
     * by the MeasureSpec.
     *
     * @param size Default size for this view
     * @param measureSpec Constraints imposed by the parent
     * @return The size this view should be.
     */
    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = EstimateSpec.getMode(measureSpec);
        int specSize = EstimateSpec.getSize(measureSpec);

        switch (specMode) {
            case EstimateSpec.UNCONSTRAINT:
                result = size;
                break;
            case EstimateSpec.NOT_EXCEED:
            case EstimateSpec.PRECISE:
                result = specSize;
                break;
        }
        return result;
    }
}
